﻿// This is an open source non-commercial project. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com

// ReSharper disable CheckNamespace
// ReSharper disable ClassNeverInstantiated.Global
// ReSharper disable CommentTypo
// ReSharper disable IdentifierTypo
// ReSharper disable InconsistentNaming
// ReSharper disable StringLiteralTypo
// ReSharper disable UnusedParameter.Local

/* BitmapByteQRCode.cs --
 * Ars Magna project, http://arsmagna.ru
 */

#region Using directives

using System;
using System.Collections.Generic;
using System.Linq;

using static AM.Drawing.QRCoding.QRCodeGenerator;

#endregion

#nullable enable

namespace AM.Drawing.QRCoding;

/// <summary>
/// QR-код в виде растрового изображения
/// </summary>
public class BitmapByteQRCode
    : AbstractQRCode,
    IDisposable
{
    #region Construction

    /// <summary>
    /// Constructor without params to be used in COM Objects connections
    /// </summary>
    public BitmapByteQRCode()
    {
        // пустое тело конструктора
    }

    /// <summary>
    /// Конструктор.
    /// </summary>
    public BitmapByteQRCode
        (
            QRCodeData data
        )
        : base (data)
    {
        // пустое тело конструктора
    }

    #endregion

    public byte[] GetGraphic(int pixelsPerModule)
    {
        return GetGraphic(pixelsPerModule, new byte[] { 0x00, 0x00, 0x00 }, new byte[] { 0xFF, 0xFF, 0xFF });
    }

    public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex)
    {
        return GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex));
    }

    public byte[] GetGraphic(int pixelsPerModule, byte[] darkColorRgb, byte[] lightColorRgb)
    {
        var sideLength = this.QrCodeData.ModuleMatrix.Count * pixelsPerModule;

        var moduleDark = darkColorRgb.Reverse();
        var moduleLight = lightColorRgb.Reverse();

        List<byte> bmp = new List<byte>();

        //header
        bmp.AddRange(new byte[] { 0x42, 0x4D, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00 });

        //width
        bmp.AddRange(IntTo4Byte(sideLength));
        //height
        bmp.AddRange(IntTo4Byte(sideLength));

        //header end
        bmp.AddRange(new byte[] { 0x01, 0x00, 0x18, 0x00 });

        //draw qr code
        for (var x = sideLength-1; x >= 0; x = x - pixelsPerModule)
        {
            for (int pm = 0; pm < pixelsPerModule; pm++)
            {
                for (var y = 0; y < sideLength; y = y + pixelsPerModule)
                {
                    var module =
                        this.QrCodeData.ModuleMatrix[(x + pixelsPerModule)/pixelsPerModule - 1][(y + pixelsPerModule)/pixelsPerModule - 1];
                    for (int i = 0; i < pixelsPerModule; i++)
                    {
                        bmp.AddRange(module ? moduleDark : moduleLight);
                    }
                }
                if (sideLength%4 != 0)
                {
                    for (int i = 0; i < sideLength%4; i++)
                    {
                        bmp.Add(0x00);
                    }
                }
            }
        }

        //finalize with terminator
        bmp.AddRange(new byte[] { 0x00, 0x00 });

        return bmp.ToArray();
    }

    private byte[] HexColorToByteArray(string colorString)
    {
        if (colorString.StartsWith("#"))
        {
            colorString = colorString.Substring(1);
        }

        byte[] byteColor = new byte[colorString.Length / 2];
        for (int i = 0; i < byteColor.Length; i++)
            byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture);
        return byteColor;
    }

    private byte[] IntTo4Byte(int inp)
    {
        byte[] bytes = new byte[2];
        unchecked
        {
            bytes[1] = (byte)(inp >> 8);
            bytes[0] = (byte)(inp);
        }
        return bytes;
    }
}


public static class BitmapByteQRCodeHelper
{
    public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex,
        string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false,
        EciMode eciMode = EciMode.Default, int requestedVersion = -1)
    {
        using (var qrGenerator = new QRCodeGenerator())
        using (
            var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode,
                requestedVersion))
        using (var qrCode = new BitmapByteQRCode(qrCodeData))
            return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex);
    }

    public static byte[] GetQRCode(string txt, QRCodeGenerator.ECCLevel eccLevel, int size)
    {
        using (var qrGen = new QRCodeGenerator())
        using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
        using (var qrBmp = new BitmapByteQRCode(qrCode))
            return qrBmp.GetGraphic(size);

    }
}
